package com.sc.nft.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.sc.nft.config.fulu.model.dto.FuLuOrderDirectDTO;
import com.sc.nft.config.fulu.model.request.FuLuOrderDirectRequest;
import com.sc.nft.dao.MallPayOrderRecordDao;
import com.sc.nft.dao.UserInfoDao;
import com.sc.nft.dao.WxMiniUserInfoDao;
import com.sc.nft.entity.*;
import com.sc.nft.entity.vo.mall.MallWxPayVO;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalException;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.feign.WxMiniPayClient;
import com.sc.nft.feign.WxMiniUserPointsClient;
import com.sc.nft.helper.SmartSupplyHelper;
import com.sc.nft.pool.UserPool;
import com.sc.nft.req.points.PointsReq;
import com.sc.nft.req.wxPay.TradeNoReq;
import com.sc.nft.req.wxPay.WxPrepayReq;
import com.sc.nft.service.*;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.sup.Result;
import com.sc.nft.util.OrderNoUtil;
import com.sc.nft.vo.profittemplate.ProfitTemplateGetProfitAndPointsVO;
import com.sc.nft.vo.wxPay.PrepayWithRequestPaymentResponse;
import com.sc.nft.vo.wxPay.RefundNotification;
import com.sc.nft.vo.wxPay.Transaction;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 微信支付记录实现
 *
 * @Author：LiGen
 * @Package：com.sc.nft.service.impl
 * @Project：sc_metago
 * @name：WxMiniPayServiceImpl
 * @Date：2024/5/16 21:43
 * @Filename：WxMiniPayServiceImpl
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class WxMiniPayServiceImpl implements WxMiniPayService {

    private final MallPayOrderRecordDao mallPayOrderRecordDao;
    private final MallPayOrderItemService mallPayOrderItemService;
    private final MallOrderService mallOrderService;
    private final MallPayOrderRecordService mallPayOrderRecordService;
    private final WxMiniPayClient wxMiniPayClient;
    private final SmartSupplyHelper smartSupplyHelper;
    private final MallOrderProductService mallOrderProductService;
    private final MallActivitySpecsService mallActivitySpecsService;
    private final MallOrderIncomeService mallOrderIncomeService;
    private final WxMiniUserPointsClient wxMiniUserPointsClient;
    private final Redisson redisson;
    private final WxMiniUserInfoDao wxMiniUserInfoDao;
    private final UserInfoDao userInfoDao;

    /**
     * 微信小程序支付记录初始化
     *
     * @param mallOrderList
     * @param wxMiniUserInfo
     * @return
     */
    @Override
    public MallPayOrderRecord PayOrderInitialize(List<MallOrder> mallOrderList, WxMiniUserInfo wxMiniUserInfo, Date now, String ip, BigDecimal totalAmount) {
        // 微信支付记录初始化
        MallPayOrderRecord mallPayOrderRecord = new MallPayOrderRecord();
        mallPayOrderRecord.setUserId(wxMiniUserInfo.getUserId());
        mallPayOrderRecord.setMiniUserId(wxMiniUserInfo.getId());
        mallPayOrderRecord.setPayType(PayMethodEnum.WX_PAY);
        mallPayOrderRecord.setReqTime(now);
        mallPayOrderRecord.setCloseTime(now);
        // 由于现在有多天订单，所以这里随机在生成一个订单编号作为父级订单编号
        mallPayOrderRecord.setOrderNo(OrderNoUtil.getOrderNo());
        mallPayOrderRecord.setReqIp(ip);
        mallPayOrderRecord.setOrderType(PayTypeEnum.PAY);
        mallPayOrderRecord.setPayAmount(totalAmount);
        mallPayOrderRecord.setRefundAmount(BigDecimal.ZERO);
        mallPayOrderRecord.setNotifyUrl(StrUtil.EMPTY);
        mallPayOrderRecord.setFrontUrl(StrUtil.EMPTY);
        mallPayOrderRecord.setOutTradeNo(OrderNoUtil.getOrderNo());
        mallPayOrderRecord.setTradeNo(StrUtil.EMPTY);
        mallPayOrderRecord.setChannel(PayChannelEnum.WX_PAY);
        mallPayOrderRecord.setReqResult(StrUtil.EMPTY);
        mallPayOrderRecord.setStatus(PayStatusEnum.INIT);
        mallPayOrderRecord.setPayResult(StrUtil.EMPTY);
        mallPayOrderRecord.setPayTime(now);
        mallPayOrderRecord.insert();
        List<MallPayOrderItem> mallPayOrderItemList = new ArrayList<>();
        for (MallOrder mallOrder : mallOrderList) {
            MallPayOrderItem mallPayOrderItem = new MallPayOrderItem();
            mallPayOrderItem.setPayOrderId(mallPayOrderRecord.getId());
            mallPayOrderItem.setOrderId(mallOrder.getId());
            mallPayOrderItemList.add(mallPayOrderItem);
        }
        mallPayOrderItemService.save(mallPayOrderItemList);
        return mallPayOrderRecord;
    }

    /**
     * 微信小程序支付
     *
     * @param orderIds
     * @param wxMiniUserInfo
     * @param ip
     * @return
     */
    @Override
    public PrepayWithRequestPaymentResponse wxMiniPrepay(List<Long> orderIds, WxMiniUserInfo wxMiniUserInfo, String ip) {
        DateTime now = DateTime.now();
        List<MallOrder> mallOrderList = mallOrderService.getByIds(orderIds, wxMiniUserInfo.getId());
        if (mallOrderList.isEmpty()) {
            throw new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND);
        }
        for (MallOrder mallOrder : mallOrderList) {
            checkPayOrder(mallOrder);
        }
        MallPayOrderRecord mallPayOrderRecord = PayOrderInitialize(mallOrderList, wxMiniUserInfo, now, ip, mallOrderList.stream().map(MallOrder::getRealPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        // 调用微信支付接口
        Result<PrepayWithRequestPaymentResponse> prepayWithRequestPaymentResponseResult = wxMiniPayClient.prepay
                (WxPrepayReq.build(wxMiniUserInfo.getId(), "闹闹商城商品", mallPayOrderRecord.getOutTradeNo(),
                        DateUtil.format(mallOrderList.get(0).getExpiredTime(), "yyyy-MM-dd'T'HH:mm:ssZ"), mallPayOrderRecord.getPayAmount()));
        if (!ErrorCode.SUCCESS.getCode().equals(prepayWithRequestPaymentResponseResult.getCode())){
            log.info("远程返回：{}", JSONUtil.toJsonStr(prepayWithRequestPaymentResponseResult));
            throw new GlobalRunTimeException("系统繁忙，请稍后再试！");
        }
        PrepayWithRequestPaymentResponse prepayWithRequestPaymentResponse = prepayWithRequestPaymentResponseResult.getData();
        prepayWithRequestPaymentResponse.setPayOrderId(mallPayOrderRecord.getId());
        return prepayWithRequestPaymentResponseResult.getData();
    }

    /**
     * 商户订单号查询订单
     *
     * @param payOrderId 支付单Id
     * @return 订单信息
     */
    @Override
     public MallWxPayVO queryWxMiniPayByOutTradeNo(Long payOrderId) {
        // 商品锁
        RLock lock = redisson.getLock(RedisKeyEnum.MALL_PAYMENT_SINGLE_SYNCHRONIZATION_LOCK.getKey() + payOrderId);
        MallWxPayVO mallWxPayVO = null;
        try {
            boolean res = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if (res) {
                // 获取支付单
                MallPayOrderRecord mallPayOrderRecord = mallPayOrderRecordDao.getById(payOrderId);
                // 支付单不存在
                Assert.notNull(mallPayOrderRecord, () -> new GlobalRunTimeException(ErrorCode.THE_PAYMENT_SLIP_DOESN_T_EXIST));
                Assert.isTrue(mallPayOrderRecord.getPayType() == PayMethodEnum.WX_PAY, () -> new GlobalRunTimeException(ErrorCode.THE_PAYMENT_SLIP_DOESN_T_EXIST));
                mallWxPayVO = new MallWxPayVO();
                mallWxPayVO.setPayOrderId(mallPayOrderRecord.getId());
                mallWxPayVO.setPayNotify(Boolean.FALSE);
                // 查询微信支付订单
                Result<Transaction> transactionResult = wxMiniPayClient.queryOrderByOutTradeNo(TradeNoReq.build(mallPayOrderRecord.getOutTradeNo()));
                if (!ErrorCode.SUCCESS.getCode().equals(transactionResult.getCode())){
                    throw new GlobalRunTimeException("系统繁忙，请稍后再试！");
                }
                Transaction transaction = transactionResult.getData();
                if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
                    syncPayOrder(transaction, mallPayOrderRecord);
                    mallWxPayVO.setPayNotify(Boolean.TRUE);
                }
            } else {
                throw new GlobalRunTimeException("系统繁忙 请稍后再试！");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            throw new GlobalRunTimeException("系统超时");
        } finally {
            lock.unlock();
        }
        return mallWxPayVO;
    }

    /**
     * 微信支付回调
     *
     * @param transaction
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity payNotify(Transaction transaction) {
        MallPayOrderRecord mallPayOrderRecordInDb = mallPayOrderRecordDao.getPayByOutTradeNo(transaction.getOutTradeNo());
        // 商品锁
        RLock lock = redisson.getLock(RedisKeyEnum.MALL_PAYMENT_SINGLE_SYNCHRONIZATION_LOCK.getKey() + mallPayOrderRecordInDb.getId());
        try {
            boolean res = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if (res) {
                // 获取支付单
                MallPayOrderRecord mallPayOrderRecord = mallPayOrderRecordDao.getById(mallPayOrderRecordInDb.getId());
                // 支付单不存在
                Assert.notNull(mallPayOrderRecord, () -> new GlobalRunTimeException(ErrorCode.THE_PAYMENT_SLIP_DOESN_T_EXIST));
                Assert.isTrue(mallPayOrderRecord.getPayType() == PayMethodEnum.WX_PAY, () -> new GlobalRunTimeException(ErrorCode.THE_PAYMENT_SLIP_DOESN_T_EXIST));
                if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
                    syncPayOrder(transaction, mallPayOrderRecord);
                }
            } else {
                throw new GlobalRunTimeException("系统繁忙 请稍后再试！");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new GlobalRunTimeException("系统超时");
        } finally {
            lock.unlock();
        }
        return ResponseEntity.status(HttpStatus.OK).build();
    }

    /**
     * 微信支付结果同步支付单 todo :细节还需处理
     *
     * @param transaction
     */
    private void syncPayOrder(Transaction transaction, MallPayOrderRecord mallPayOrderRecord) {
        log.info("微信支付同步：微信返回:{},支付单:{}", transaction, mallPayOrderRecord);
        if (PayStatusEnum.INIT == mallPayOrderRecord.getStatus()) {
            // 只有当支付单为初始化的时候 才能够去同步
            if (Transaction.TradeStateEnum.SUCCESS == transaction.getTradeState()) {
                // 支付成功
                mallPayOrderRecord.setWxTransactionId(transaction.getTransactionId());
                mallPayOrderRecord.setPaySuccessTime(DateUtil.parse(transaction.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ss"));
                mallPayOrderRecord.setStatus(PayStatusEnum.SUCCESS);
                mallPayOrderRecord.setPayResult(JSON.toJSONString(transaction));
                mallPayOrderRecord.updateById();
                log.info("微信支付单已同步同步：支付单:{}", mallPayOrderRecord);
                // 支付成功后的操作
                List<MallOrder> mallOrderList = mallPayOrderItemService.getOrderListByPayOrderId(mallPayOrderRecord.getId());
                for (MallOrder mallOrder : mallOrderList) {
                    orderPaySuccess(mallOrder, mallPayOrderRecord);
                }
            }
        }
    }

    private void orderPaySuccess(MallOrder order, MallPayOrderRecord payOrder) {
        //修改订单
        order.setPayTime(payOrder.getPaySuccessTime());
        order.setPayType(PayMethodEnum.WX_PAY);
        order.setStatus(MallOrderStatusEnum.WAIT_RECEIPT);
        order.updateById();
        if (StrUtil.isNotBlank(order.getSupplyOrderNo())) {
            smartSupplyHelper.confirmOrder(order);
        }
        //获取订单商品
        List<MallOrderProduct> orderProdcutList = mallOrderProductService.getListByOrderId(order.getId());
        for (MallOrderProduct mallOrderProduct : orderProdcutList) {
            //4.扣除库存
            Assert.isTrue(mallActivitySpecsService.frozenConsumeStockByIdAndCount(mallOrderProduct.getActivitySpecsId(), mallOrderProduct.getProductNum()), () -> new GlobalRunTimeException(ErrorCode.THE_SYSTEM_IS_BUSY_PLEASE_TRY_AGAIN_LATER));
        }
        WxMiniUserInfo wxMiniUser = wxMiniUserInfoDao.getById(order.getMiniUserId());
        //分销计算的
        ProfitTemplateGetProfitAndPointsVO profitTemplateGetProfitAndPointsVO = new ProfitTemplateGetProfitAndPointsVO();
        Long recommendedWxMiniUserId = null;
        // 计算收益
        if (Objects.nonNull(order.getRecommendedUserId())){
            UserInfo userInfo = userInfoDao.getById(order.getUserId());
            if (Objects.nonNull(userInfo.getRecommendUserId())){
                WxMiniUserInfo info = wxMiniUserInfoDao.getByUserId(userInfo.getRecommendUserId());
                if (Objects.nonNull(info)){
                    recommendedWxMiniUserId = info.getId();
                }
            }
            // 分销订单
            profitTemplateGetProfitAndPointsVO = mallOrderIncomeService.wxMiniNewCalCulateIncome(orderProdcutList, order, wxMiniUser);
        }
        // 下单拿积分
        wxMiniUserPointsClient.addPoints(PointsReq.buildBuySelfAndFriends(wxMiniUser.getId(),recommendedWxMiniUserId, order.getId(), order.getRealPrice(), order.getCreateTime(),profitTemplateGetProfitAndPointsVO.getPoints()));

    }

    private void checkPayOrder(MallOrder mallOrder) {
        Assert.notNull(mallOrder, () -> new GlobalRunTimeException(ErrorCode.ORDER_NOT_FOUND));
        Assert.isTrue(DateTime.now().isBefore(mallOrder.getExpiredTime()), () -> new GlobalRunTimeException(ErrorCode.ORDER_STATUS_ERROR));
        Assert.isTrue(mallOrder.getStatus() == MallOrderStatusEnum.WAIT_PAY, () -> new GlobalRunTimeException(ErrorCode.ORDER_STATUS_ERROR));
        Assert.isNull(mallPayOrderRecordService.getPaySuccessByOrderNo(mallOrder.getOrderNo()), () -> new GlobalRunTimeException(ErrorCode.ORDER_PAID));
    }
}
